import EventEmitter from 'events';
import log4js from 'koa-log4';

// incoming messages
import loginHandler from './incoming/login';
import messageHandler from './incoming//message';
import incomeEvents from './incoming/eventTypes';

// outgoing messages
import channelJoinedHandler from './outgoing/channelJoined';
import channelLeftHandler from './outgoing/channelLeft';
import clientStatusHandler from './outgoing/clientStatus';
import outgoingEvents from './outgoing/eventTypes';
import urgentTeacherReplacement from './outgoing/urgentTeacherReplacement';

import {
  REDIS_SYSTEM_CHANNEL,
  REDIS_SKYNET_LOGIN,
  REDIS_SKYNET_LOGOUT,
  REDIS_JANUS_LOGIN,
  REDIS_JANUS_LOGOUT,
  URGENT_TEACHER_REPLACEMENT,
} from '../common/redisChannels';

const socketLogger = log4js.getLogger('SOCK');
class MessageEmitter extends EventEmitter {
  constructor(...args) {
    super(...args);
    this.incomeHandleMappings = {
      [incomeEvents.LOGIN]: loginHandler,
      [incomeEvents.MESSAGE]: messageHandler,
    };

    this.outgoingHandleMapping = {
      [outgoingEvents.CHANNEL_JOINED]: channelJoinedHandler,
      [outgoingEvents.CHANNEL_LEFT]: channelLeftHandler,
    };

    this.redisTopicHandlerMapping = {
      [REDIS_SKYNET_LOGIN]: clientStatusHandler,
      [REDIS_SKYNET_LOGOUT]: clientStatusHandler,
      [REDIS_JANUS_LOGIN]: clientStatusHandler,
      [REDIS_JANUS_LOGOUT]: clientStatusHandler,
      [URGENT_TEACHER_REPLACEMENT]: urgentTeacherReplacement,
    };
  }

  static isValidHandler(handler) {
    return handler && typeof handler.handle === 'function';
  }

  handleIncomeMessage(eventType, ctx, data) {
    const handler = this.incomeHandleMappings[eventType];
    const callArgs = {
      eventType,
      ctx,
      data,
      emitter: this,
    };

    if (MessageEmitter.isValidHandler(handler)) {
      handler.handle(callArgs);
    }
  }

  handleRedisMessage(topic, payload, ctx) {
    let parsedPayload = null;
    try {
      parsedPayload = JSON.parse(payload);
    } catch (e) {
      socketLogger.error(`Parse redis topic ${topic} with payload ${payload} fail, skip emitting`);
      return;
    }

    if (MessageEmitter.isIrregularTopic(topic, this.redisTopicHandlerMapping)) {
      // redis topic为主
      this.handleIrregularRedisMessage(topic, ctx, parsedPayload);
    } else if (MessageEmitter.isNoticeRedisMessage(topic, ctx)) {
      this.handleNoticeRedisMessage(ctx, topic, payload, parsedPayload);
    } else {
      socketLogger.info(`${ctx.socket
        .id} <<< MESSAGE ${topic} subType:${parsedPayload.subType} body:${parsedPayload.body}`);
      ctx.socket.emit(outgoingEvents.MESSAGE, parsedPayload);
    }
  }

  handleIrregularRedisMessage(topic, ctx, parsedPayload) {
    const handler = this.redisTopicHandlerMapping[topic];
    if (!MessageEmitter.isValidHandler(handler)) return;

    handler.handle(ctx, parsedPayload, { topic });
  }

  handleNoticeRedisMessage(ctx, topic, payload, parsedPayload) {
    // socketLogger.info(`${ctx.socket.id} <<< NOTICE ${topic} ${payload}`);
    const handler = this.outgoingHandleMapping[parsedPayload.event];
    ctx.socket.emit(parsedPayload.event, parsedPayload.payload);

    if (!MessageEmitter.isValidHandler(handler)) return;
    handler.handle(ctx, parsedPayload.payload, { topic });
  }

  static isIrregularTopic(topic, irregularTopics) {
    return Boolean(irregularTopics[topic]);
  }

  static isNoticeRedisMessage(topic, ctx) {
    return (
      topic === REDIS_SYSTEM_CHANNEL ||
      topic === ctx.viewer.getRedisUserNoticeChannelName() ||
      topic.indexOf('group') >= 0
    );
  }

  initIncomeMessage(fn) {
    Object.keys(this.incomeHandleMappings).forEach((incomeEvent) => {
      fn(incomeEvent, (ctx, data) => this.handleIncomeMessage(incomeEvent, ctx, data));
    });
  }
}

const incomeMessageEmitter = new MessageEmitter();

export default incomeMessageEmitter;
